﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using DriveBeamEditor.Controls.BeamNG.Forms;
using DriveBeamEditor.Controls.General.Popup;
using JBeamLibrary;
using JBeamLibrary.Tools;
using JBeamLibrary.Types;
using JBeamLibrary.VehicleParts;

namespace DriveBeamEditor.Controls.BeamNG.Popups
{
    public partial class VehiclePopupMenu : PopupContainer
    {
        #region Fields

        private Vehicle _currentVehicle;

        public event EventHandler<Vehicle> VehicleOpened;
        public event EventHandler<Vehicle> VehicleSaved;

        #endregion

        #region Properties

        /// <summary>Gets or sets the current vehicle</summary>
        public Vehicle CurrentVehicle
        {
            get { return _currentVehicle; }
            set
            {
                if (_currentVehicle == value) return;
                _currentVehicle = value;
                if (_currentVehicle != null)
                {
                    var tag = new TaggedString(_currentVehicle.Label, _currentVehicle.Name);
                    if (vehicleList.Items.Contains(tag))
                    {
                        vehicleList.SelectedItem = tag;
                    }
                    else
                    {
                        SelectedVehicleName = _currentVehicle.Name;
                        SelectedVehicleLabel = _currentVehicle.Label;
                    }
                }
            }
        }

        /// <summary>Gets or sets the selected vehicle name</summary>
        public string SelectedVehicleName
        {
            get { return kvcName.EditValue.ToStringOrEmpty(); }
            set { kvcName.EditValue = value.ToStringOrEmpty(); }
        }

        /// <summary>Gets or sets the selected vehicle label</summary>
        public string SelectedVehicleLabel
        {
            get { return kvcLabel.EditValue.ToStringOrEmpty(); }
            set { kvcLabel.EditValue = value.ToStringOrEmpty(); }
        }

        #endregion

        #region CTORs

        public VehiclePopupMenu()
        {
            InitializeComponent();
        }

        #endregion

        #region Events

        #region Overrides

        protected override void OnLoad(EventArgs e)
        {
            if (!DesignMode)
                RefreshVehicles();
            base.OnLoad(e);
        }

        #endregion

        #region Buttons

        private void xbOpen_Click(object sender, EventArgs e)
        {
            OpenCurrentVehicle();
        }

        private void xbOpen_MouseClick(object sender, MouseEventArgs e)
        {
        }

        private void xbSave_Click(object sender, EventArgs e)
        {
            SaveCurrentVehicle();
        }

        private void xbReload_Click(object sender, EventArgs e)
        {
            RefreshVehicles();
        }

        #endregion

        #region Vehicle list

        private void vehicleList_SelectedIndexChanged(object sender, EventArgs e)
        {
            SelectedVehicleName = vehicleList.SelectedIndex >= 0 ? ((TaggedString)vehicleList.SelectedItem).Tag.ToStringOrEmpty() : null;
            SelectedVehicleLabel = vehicleList.SelectedIndex >= 0 ? ((TaggedString)vehicleList.SelectedItem).String.ToStringOrEmpty() : null;
        }

        #endregion

        #region KeyValueControls

        private void kvcLabel_Validated(object sender, EventArgs e)
        {
            if (CurrentVehicle != null)
                CurrentVehicle.Label = kvcLabel.EditValue.ToStringOrEmpty();
        }

        #endregion

        #endregion

        #region Methods

        public async void OpenCurrentVehicle()
        {
            /* No vehicle specified */
            if (string.IsNullOrEmpty(SelectedVehicleName))
            {
                MessageBox.Show(this, @"Please select the vehicle you want to load", @"No vehicle selected",
                    MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            /* Vehicle not found */
            if (!vehicleList.Items.OfType<TaggedString>().Any(ts => ts.Tag.Equals(SelectedVehicleName)))
            {
                MessageBox.Show(this, @"The vehicle specified could not be found", @"Vehicle not found",
                    MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            /* Load the vehicle */
            try
            {
                CurrentVehicle = await Task.Run(() => Vehicle.LoadVehicle(SelectedVehicleName));
            }
            catch (InvalidDataException ex)
            {
                Debug.Assert(ex.InnerException == null, ex.Message, ex.InnerException.StackTrace);
                MessageBox.Show(ex.Message, @"Invalid data found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            /* Notify errors */
            if (CurrentVehicle.HasErrors)
            {
                MessageBox.Show(this, CurrentVehicle.GetErrorText(), @"Errors found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }

            /* Notify */
            if (VehicleOpened != null)
                VehicleOpened(this, CurrentVehicle);
        }

        public void SaveCurrentVehicle()
        {
            /* No vehicle to save */
            if (CurrentVehicle == null)
            {
                /* Do nothing if the user didn't provide a new and label */
                if (string.IsNullOrEmpty(SelectedVehicleName) || string.IsNullOrEmpty(SelectedVehicleLabel))
                    return;

                /* Request the creation of a new vehicle */
                var result = MessageBox.Show(this, string.Format(@"There is no vehicle open currently.{0}Would you like to create a new one ?", Environment.NewLine),
                    @"No vehicle open", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation);
                if (result != DialogResult.Yes) return;

                /* Ask if resources should be copied from another vehicle */
                string copySource = null;
                using (var dialog = new CopyResourcesForm { StartPosition = FormStartPosition.CenterScreen })
                {
                    if (dialog.ShowDialog(this) == DialogResult.OK && dialog.ShouldCopyResources)
                        copySource = dialog.SelectedVehicleName;
                }

                /* Create a new vehicle */
                var vehicle = new Vehicle(SelectedVehicleName) { Label = SelectedVehicleLabel };

                /* Create a default file */
                vehicle.Add(new VehicleFile(SelectedVehicleName, vehicle));

                /* Create a default part */
                var part = new VehiclePart(SelectedVehicleName, vehicle[0]) { Description = SelectedVehicleLabel };
                part.AddAuthor(Config.UserName);
                vehicle[0].Add(part);

                /* Save the vehicle as */
                CurrentVehicle = vehicle;
                Vehicle.SaveVehicle(CurrentVehicle, true);

                /* Copy resources from selected other vehicle */
                if (copySource != null)
                    Vehicle.CopyResources(copySource, vehicle);

                /* Notify */
                if (VehicleSaved != null)
                    VehicleSaved(this, CurrentVehicle);

                /* Open it right after saving */
                RefreshVehicles();
                OpenCurrentVehicle();
                return;
            }

            /* Compressed or readable */
            var readable = MessageBox.Show(
                string.Format(@"Would you like the files to be indented to be readable ?{0}Selecting 'No' will compress the files to make them as small as possible.", Environment.NewLine),
                @"Make readable ?",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question,
                MessageBoxDefaultButton.Button1
            );

            /* Save as */
            var tag = new TaggedString(SelectedVehicleLabel, SelectedVehicleName);
            if (vehicleList.Items.OfType<TaggedString>().Any(ts => ts.Tag.Equals(SelectedVehicleName)))
            {
                /* Save current vehicle */
                if (tag.Equals((TaggedString)vehicleList.SelectedItem) && CurrentVehicle.Name.Equals(tag.Tag as string))
                {
                    /* Save the vehicle as */
                    Vehicle.SaveVehicle(CurrentVehicle, readable == DialogResult.No);

                    /* Notify */
                    if (VehicleSaved != null)
                        VehicleSaved(this, CurrentVehicle);
                    return;
                }

                /* Ask to overwrite */
                var label = vehicleList.Items.OfType<TaggedString>().First(ts => ts.Tag.Equals(SelectedVehicleName)).String;
                var name = vehicleList.Items.OfType<TaggedString>().First(ts => ts.Tag.Equals(SelectedVehicleName)).Tag.ToStringOrEmpty();
                if (MessageBox.Show(
                    this,
                    string.Format("Are you sure you want to overwrite vehicle '{0} ({1})' with the contents of vehicle '{2} ({3})' ?", label, name, CurrentVehicle.Label, CurrentVehicle.Name),
                    @"Overwrite",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question) != DialogResult.Yes)
                {
                    return;
                }
            }

            /* Save the vehicle with the specified name (or overwrite) */
            Vehicle.SaveVehicleAs(CurrentVehicle, SelectedVehicleName, readable == DialogResult.No);

            /* Notify */
            if (VehicleSaved != null)
                VehicleSaved(this, CurrentVehicle);

            /* Refresh list */
            RefreshVehicles();
        }

        /// <summary>Reloads the valid vehicles into the vehicles list</summary>
        public void RefreshVehicles()
        {
            /* Add valid vehicles to the list */
            vehicleList.Items.Clear();
            vehicleList.Items.AddRange(Vehicle.GetValidVehicles().Cast<object>().ToArray());
        }

        #endregion
    }
}
